package Canile_de_apa;

import java.util.Stack;
import java.util.Vector;

public class RezolvareInLargime {
	@SuppressWarnings("rawtypes")
	Stack btStack=new Stack();
	Operator op[]={new UmpleC1(),new GolesteC1(),new Goleste12(),new UmpleC2(),new GolesteC2(),new
			Goleste21()};
	int opp; // indicele operatorului folosit pentru a obtine nodul scop
	@SuppressWarnings("unchecked")
	public void solving(Cana stare)
	{
		//Expanadare : obtin toate nodurile fiu ale nodului curent
		Vector<Cana> cani=expand2(stare);
		//Verifcare daca nodurile expandate sunt "aproape" de solutie
		// Daca se gaseste un nod anterior expandat "lnga solutie" atunci inchei procesul de cautare
		// si introduc in stiva nodul expandat, si nodul de tip scop
		for(int i=0;i<cani.size();i++)
		{
			if(langaSolutie(cani.elementAt(i))){
				btStack.add(cani.elementAt(i)); // adaug nodul anterior expanadat in stiva solutiei
				Cana c=op[opp].execute(cani.elementAt(i)); // obtin nodul scop
				btStack.add(c); // adaug nodul scop in stiva solutiei
				return;
			}
		}
		Cana s=expand(stare);
		/** Daca nodul expandat nu se mai gaseste in stiva starilor atunci */
		if(!search(s))
		{ /* Adaug noul nod in stiva */
			btStack.push(s);
			/* Verific daca noul nod adaugat in stiva este solutie */
			if(solutie(s))
				return;
			else // daca noul nod nu este solutie atunci continui cautarea
			{
				for(int i=0;i<6;i++)
					op[i].skip=false;
				solving(s);
			}
		}
		else // in caz ca nodul expandat este deja in stiva
			if(btStack.size()!=0){
				Cana c=(Cana)btStack.elementAt(btStack.size()-1);
				solving(c);
			}
	}
	public Cana expand(Cana stare){
		Cana temp=new Cana(stare.x, stare.y);
		if(op[0].valabil(temp) && op[0].skip==false)
		{
			temp=op[0].execute(temp);
			op[0].skip=true;
		}
		else if(op[1].valabil(temp)&& op[1].skip==false)
		{
			temp=op[1].execute(temp);
			op[1].skip=true;
		}
		else if(op[2].valabil(temp) && op[2].skip==false)
		{
			temp=op[2].execute(temp);
			op[2].skip=true;
		}
		else if(op[3].valabil(temp) && op[3].skip==false)
		{
			temp=op[3].execute(temp);
			op[3].skip=true;
		}
		else if(op[4].valabil(temp) && op[4].skip==false)
		{
			temp=op[4].execute(temp);
			op[4].skip=true;
		}
		else if(op[5].valabil(temp)&& op[5].skip==false)
		{
			temp=op[5].execute(temp);
			op[5].skip=true;
		}
		return temp;
	}
	public Vector<Cana> expand2(Cana stare){
		Cana temp=new Cana(stare.x, stare.y);
		Vector<Cana> cani=new Vector<Cana>();
		for(int i=0;i<6;i++)
			if(op[i].valabil(temp)&& op[i].skip==false)
			{
				cani.add(op[i].execute(temp));
			}
		return cani;
	}
	/** Aceasta metoda este folosita pentru a spune daca din starea s furnizata ca parametru se poate ajunge prin
		aplicarea unui operator la starea scop*/
	/** Cu alte cuvinte aceasta metoda spune daca starea s este vecina cu starea solutie sau scop */
	/** Metoda returneaza true daca starea s "este langa" solutie */
	public boolean langaSolutie(Cana s){
		boolean r=false;
		for(int i=0;i<6;i++)
			if(op[i].valabil(s))
				if(solutie(op[i].execute(s))){
					r=true;
					opp=i;
				}
		return r;
	}
	public boolean solutie(Cana s){
		boolean r=false;
		if(s.getY()==2)
			r=true;
		return r;
	}
	public void print(){
		if(btStack.size()!=0){
			int n= btStack.size();
			Cana sol[]=new Cana[n];
			for(int i=n-1;i>-1;i--)
				sol[i]=(Cana)btStack.pop();
			for(int i=0;i<n;i++)
				System.out.println(sol[i]);
		}
	}
	@SuppressWarnings("unchecked")
	public void init(){
		btStack.push(new Cana(0,0));
	}
	public boolean search(Cana s){
		boolean r=false;
		for(int i=0;i<btStack.size();i++)
			if(((Cana)btStack.elementAt(i)).equals(s))
			{
				return true;
			}
		return r;
	}
}